package cz.muni.fi.pa165.sme.services.impl;

import java.util.ArrayList;
import java.util.List;

import cz.muni.fi.pa165.sme.api.SportActivityRecordService;
import cz.muni.fi.pa165.sme.api.to.SportActivityRecordTO;
import cz.muni.fi.pa165.sme.api.to.SportActivityTO;
import cz.muni.fi.pa165.sme.api.to.UserTO;
import cz.muni.fi.pa165.sme.common.TOValidator;
import cz.muni.fi.pa165.sme.dao.api.SportActivityRecordDao;
import cz.muni.fi.pa165.sme.dao.entity.SportActivity;
import cz.muni.fi.pa165.sme.dao.entity.SportActivityRecord;
import cz.muni.fi.pa165.sme.dao.entity.User;
import cz.muni.fi.pa165.sme.exceptions.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.convert.ConversionService;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author jirankova
 */
@Service
public class SportActivityRecordServiceImpl implements SportActivityRecordService {

	private static final String NULL_OBJECT_MESSAGE = "Parameter cannot be null";
	private static final String ID_NULL = "Id cannot be null";
	private static final String ID_NOT_NULL = "Id must not be set";

	@Autowired
	@Qualifier("conversionService")
	private ConversionService conversionService;

	@Autowired
	private SportActivityRecordDao sportActivityRecordDao;

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	@Override
	@PreAuthorize("hasRole('USER')")
	public void createSportActivityRecord(final SportActivityRecordTO sportActivityRecord) {
		TOValidator.validateObjectNotNull(sportActivityRecord, NULL_OBJECT_MESSAGE);
		TOValidator.validateIdNull(sportActivityRecord.getId(), ID_NOT_NULL);
		final SportActivityRecord activityRecord = conversionService.convert(sportActivityRecord, SportActivityRecord.class);
		sportActivityRecordDao.createSportActivityRecord(activityRecord);
		if(activityRecord.getId() == null) {
			throw new ServiceException("ID was not assigned");
		}
		sportActivityRecord.setId(activityRecord.getId());
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	@Override
	@PreAuthorize("hasRole('USER')")
	public void deleteSportActivityRecord(final SportActivityRecordTO sportActivityRecord) {
		TOValidator.validateObjectNotNull(sportActivityRecord, NULL_OBJECT_MESSAGE);
		TOValidator.validateObjectNotNull(sportActivityRecord.getId(), ID_NULL);
		final SportActivityRecord activityRecord = conversionService.convert(sportActivityRecord, SportActivityRecord.class);
		sportActivityRecordDao.deleteSportActivityRecord(activityRecord);
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW)
	@Override
	@PreAuthorize("hasRole('USER')")
	public void updateSportActivityRecord(final SportActivityRecordTO sportActivityRecord) {
		TOValidator.validateObjectNotNull(sportActivityRecord, NULL_OBJECT_MESSAGE);
		TOValidator.validateObjectNotNull(sportActivityRecord.getId(), ID_NULL);
		final SportActivityRecord activityRecord = conversionService.convert(sportActivityRecord, SportActivityRecord.class);
		sportActivityRecordDao.updateSportActivityRecord(activityRecord);
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
	@Override
	@PreAuthorize("hasRole('USER')")
	public SportActivityRecordTO findSportActivityRecordById(final Long id) {
		TOValidator.validateObjectNotNull(id, NULL_OBJECT_MESSAGE);
		final SportActivityRecord activityRecord = sportActivityRecordDao.findSportActivityRecordById(id);
		return conversionService.convert(activityRecord, SportActivityRecordTO.class);
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
	@Override
	@PreAuthorize("hasRole('USER')")
	public List<SportActivityRecordTO> findAllSportActivityRecordsByUser(final UserTO userTO) {
		TOValidator.validateObjectNotNull(userTO, NULL_OBJECT_MESSAGE);
		TOValidator.validateObjectNotNull(userTO.getId(), ID_NULL);
		final User user = conversionService.convert(userTO, User.class);
		final List<SportActivityRecord> activityRecords = sportActivityRecordDao.findAllSportActivityRecordsByUser(user);
		return convertActivityRecords(activityRecords);
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
	@Override
	@PreAuthorize("hasRole('USER')")
	public List<SportActivityRecordTO> findAllSportActivityRecordsBySportActivity(final SportActivityTO sportActivity) {
		TOValidator.validateObjectNotNull(sportActivity, NULL_OBJECT_MESSAGE);
		TOValidator.validateObjectNotNull(sportActivity.getId(), ID_NULL);
		final SportActivity activity = conversionService.convert(sportActivity, SportActivity.class);
		final List<SportActivityRecord> activityRecords = sportActivityRecordDao.findAllSportActivityRecordsBySportActivity(activity);
		return convertActivityRecords(activityRecords);
	}

	@Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
	@Override
	@PreAuthorize("hasRole('USER')")
	public List<SportActivityRecordTO> findAllSportActivityRecords() {
		final List<SportActivityRecord> activityRecords = sportActivityRecordDao.findAllSportActivityRecords();
		return convertActivityRecords(activityRecords);
	}

	@Override
	@Transactional(readOnly = true)
	public boolean exists(final SportActivityRecordTO sportActivityRecordTO) {
		TOValidator.validateObjectNotNull(sportActivityRecordTO, NULL_OBJECT_MESSAGE);
		if (sportActivityRecordTO.getId() == null) {
			return false;
		}
		return sportActivityRecordDao.findSportActivityRecordById(sportActivityRecordTO.getId()) != null;
	}

	private List<SportActivityRecordTO> convertActivityRecords(final List<SportActivityRecord> source) {
		if(source == null) {
			return null;
		}

		final List<SportActivityRecordTO> target = new ArrayList<SportActivityRecordTO>(source.size());

		for (SportActivityRecord activityRecord : source) {
			target.add(conversionService.convert(activityRecord, SportActivityRecordTO.class));
		}

		return target;
	}
}
